﻿using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;

namespace HeritageAcademy.Library.Controls
{
    public class PagingDecorator : Decorator
    {
        #region Parameters 
        Size _availableSize;
        DispatcherTimer _resizeTimer;
        #endregion 

        #region Event Handlers 
        void resizeTimer_Tick(object sender, EventArgs e)
        {
            _resizeTimer.Stop();
            GotoPage(CurrentPage);
        }
        #endregion 

        #region Public Methods 
        public void GotoPage(int pageIndex)
        {
            pageIndex = Math.Min(pageIndex, PageCount - 1);
            pageIndex = Math.Max(pageIndex, 0);

            CurrentPage = pageIndex;

            var offset = _availableSize.Width * pageIndex * -1;

            // construct an animation that will update the RenderTransform on the child
            var t = Child.RenderTransform as TranslateTransform;
            if (t == null)
            {
                t = new TranslateTransform();
                Child.RenderTransform = t;
            }

            var d = new DoubleAnimationUsingKeyFrames();
            var kf = new SplineDoubleKeyFrame(offset, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(TransitionDuration)))
                         {KeySpline = TransitionKeySpline};
            d.KeyFrames.Add(kf);

            t.BeginAnimation(TranslateTransform.XProperty, d);
        }
        #endregion

        #region Protected Methods 
        protected override Size ArrangeOverride(Size finalSize)
        {
            // calculate the number of pages
            PageCount = (int)Math.Ceiling(Child.DesiredSize.Width / _availableSize.Width);

            Child.Arrange(new Rect(0, 0, Child.DesiredSize.Width, Child.DesiredSize.Height));

            if (_resizeTimer == null)
            {
                _resizeTimer = new DispatcherTimer {Interval = TimeSpan.FromMilliseconds(250)};
                _resizeTimer.Tick += resizeTimer_Tick;
            }
            _resizeTimer.Stop();
            _resizeTimer.Start();

            return _availableSize;
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            _availableSize = availableSize;
            Child.Measure(new Size(double.PositiveInfinity, availableSize.Height));
            return Child.DesiredSize;
        }
        #endregion

        #region PageCount
        /// <summary>
        /// PageCount Dependency Property
        /// </summary>
        public static readonly DependencyProperty PageCountProperty =
            DependencyProperty.Register("PageCount", typeof(int), typeof(PagingDecorator),
                new FrameworkPropertyMetadata(-1,
                    new PropertyChangedCallback(OnPageCountChanged)));

        /// <summary>
        /// Gets or sets the PageCount property.  This dependency property 
        /// indicates ....
        /// </summary>
        public int PageCount
        {
            get { return (int)GetValue(PageCountProperty); }
            set { SetValue(PageCountProperty, value); }
        }

        /// <summary>
        /// Handles changes to the PageCount property.
        /// </summary>
        private static void OnPageCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((PagingDecorator)d).OnPageCountChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the PageCount property.
        /// </summary>
        protected virtual void OnPageCountChanged(DependencyPropertyChangedEventArgs e)
        {
            var count = (int)e.NewValue;

            // update the collection
            if (PageCollection == null)
                PageCollection = new ObservableCollection<NumberedPage>();

            while (PageCollection.Count > count)
                PageCollection.RemoveAt(PageCollection.Count - 1);

            while (PageCollection.Count < count)
                PageCollection.Add(new NumberedPage((count - (count - PageCollection.Count) + 1)));

            CurrentPage = Math.Min(CurrentPage, count - 1);
        }
        #endregion

        #region TransitionKeySpline
        /// <summary>
        /// TransitionKeySpline Dependency Property
        /// </summary>
        public static readonly DependencyProperty TransitionKeySplineProperty =
            DependencyProperty.Register("TransitionKeySpline", typeof(KeySpline), typeof(PagingDecorator),
                new FrameworkPropertyMetadata(new KeySpline(0, 0.5, 0.5, 1)));

        /// <summary>
        /// Gets or sets the TransitionKeySpline property.  This dependency property 
        /// indicates ....
        /// </summary>
        public KeySpline TransitionKeySpline
        {
            get { return (KeySpline)GetValue(TransitionKeySplineProperty); }
            set { SetValue(TransitionKeySplineProperty, value); }
        }
        #endregion

        #region CurrentPage
        /// <summary>
        /// CurrentPage Dependency Property
        /// </summary>
        public static readonly DependencyProperty CurrentPageProperty =
            DependencyProperty.Register("CurrentPage", typeof(int), typeof(PagingDecorator),
                new FrameworkPropertyMetadata(0,
                    new PropertyChangedCallback(OnCurrentPageChanged)));

        /// <summary>
        /// Gets or sets the CurrentPage property.  This dependency property 
        /// indicates ....
        /// </summary>
        public int CurrentPage
        {
            get { return (int)GetValue(CurrentPageProperty); }
            set { SetValue(CurrentPageProperty, value); }
        }

        /// <summary>
        /// Handles changes to the CurrentPage property.
        /// </summary>
        private static void OnCurrentPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((PagingDecorator)d).OnCurrentPageChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the CurrentPage property.
        /// </summary>
        protected virtual void OnCurrentPageChanged(DependencyPropertyChangedEventArgs e)
        {
            GotoPage((int)e.NewValue);
        }
        #endregion

        #region PageCollection
        /// <summary>
        /// PageCollection Dependency Property
        /// </summary>
        public static readonly DependencyProperty PageCollectionProperty =
            DependencyProperty.Register("PageCollection", typeof(ObservableCollection<NumberedPage>), typeof(PagingDecorator),
                new FrameworkPropertyMetadata((ObservableCollection<NumberedPage>)null));

        /// <summary>
        /// Gets or sets the PageCollection property.  This dependency property 
        /// indicates ....
        /// </summary>
        public ObservableCollection<NumberedPage> PageCollection
        {
            get { return (ObservableCollection<NumberedPage>)GetValue(PageCollectionProperty); }
            set { SetValue(PageCollectionProperty, value); }
        }
        #endregion

        #region TransitionDuration
        /// <summary>
        /// TransitionDuration Dependency Property
        /// </summary>
        public static readonly DependencyProperty TransitionDurationProperty =
            DependencyProperty.Register("TransitionDuration", typeof(int), typeof(PagingDecorator),
                new FrameworkPropertyMetadata(600));

        /// <summary>
        /// Gets or sets the TransitionDuration property.  This dependency property 
        /// indicates ....
        /// </summary>
        public int TransitionDuration
        {
            get { return (int)GetValue(TransitionDurationProperty); }
            set { SetValue(TransitionDurationProperty, value); }
        }
        #endregion
    }

    public class NumberedPage
    {
        public NumberedPage(int pageNumber)
        {
            PageNumber = pageNumber;
        }

        public int PageNumber { get; set; }
    }
}
